<?

//benchmark('test_concat,test_implode',4000);
 
 require_once('functions.mbstring.php');
 
// benchmark($func_list, $repeat=1000) 
// сравнивает скорость выпонения нескольких функций
// и выводит отчет о выполнении 
// пример:  benchmark('a,b,c,d,e');
function benchmark($func_list, $repeat=1000)
{
	// $funcs = explode(',', 'benchmark_empty,'.$func_list);
	$funcs = explode(',', $func_list);
	$time=array();
	$start_all = microtime(true);
	
	// пропускаем каждую функцию через цикл и измеряем время выполнения
	foreach ($funcs AS $func)
	{
		$func=trim($func);
		
		// echo "<br/>func = $func<br/>";
		
		if (!$func OR !function_exists($func)) continue;
		
		$start = microtime(true)-$start_all;
		for($i=0; $i<$repeat; $i++) $val = $func();
		$end = microtime(true)-$start_all;
		
		// echo "start=$start<br/>";
		// echo "end=$end<br/>";
		
		$time[$func] = $end - $start;
		
		// echo "delta = ".$time[$func]."<br/>";
	}
	
	
	echo "Результаты тестирования:<br/><br/>


		<table border=1 cellspacing=0 cellpadding=3 width=220 >";
	
	//$emty_time = array_shift($time);
	asort($time);
	foreach ($time as $f=>&$t) 
	{
		//$t -= $emty_time;
		$t = round(1000*$t,2);
		
		echo "<tr><td><b>$f</b>&nbsp;&nbsp;<td align=right nowrap>&nbsp;$t ms";
		
	}
	echo "</table><br/><br/>";
	exit;
}

function benchmark_empty(){}



// извлекает значние из массива $_GET, без вывода ошибок 
function GET($key, $default=null)
{
	return isset($_GET[$key])? $_GET[$key] :$default;
}	

// извлекает значние из массива $_POST, без вывода ошибок 
function POST($key, $default=null)
{
	return isset($_POST[$key])? $_POST[$key] :$default;
}	

// извлекает значние из массива COOKIE $_COOKIE , без вывода ошибок 
function COOKIE($key, $default=null)
{
	return isset($_COOKIE[$key])? $_COOKIE[$key] :$default;
}	


// val(&$var) - возвращает значение переменной, а если она не установлена - возвращает значение $default или null
function val(&$var, $default=null)
{
	if (isset($var)) return $var;
	else return $default;
}
//test: var_dump( val($_GET)); exit;
/*
$a=array();
echo val($a['d'], 1);
echo val($a['d'], 1);
var_dump($a);
exit;
pr(val($a, 1));
*/

// find_is_set(&$var1, &$var2, ... ) - возвращает первое установленное значение из списка переменных или null, если такое не найдено
function find_is_set(&$var1, &$var2=null, &$var3=null, &$var4=null, &$var5=null, &$var6=null, &$var7=null, &$var8=null, &$var9=null, &$var10=null )
{
    for($i=1; $i<=10; $i++) {
	
	
		if (isset(${'var'.$i}) && null!==${'var'.$i} ) return ${'var'.$i};
	}
	return null;	
}
//test:
//$d=4;
//pr(var_dump(find_is_set($a,$b,$c,$d)));


/*** find_not_empty($val1, $val2,... )  - возвращает первое непустое значение из списка аргументов; если не найдено, возвращает false. 
		
Чтобы проверять переменные, которые, возможно, несуществуют (неустановлены), их нужно передавать по ссылке. Пример:  $mode = find_not_empty(&$mode, &Core::$Config->mode , &$_GET['mode'], 'mode_default'); 
***/
function find_not_empty()
{
    $vars = func_get_args();
    foreach ($vars as $v) 
	{	
		$v=trim($v); if (!empty($v)) return $v;
	}
	return false;
}


function not_empty()
{
    $vars = func_get_args();
    foreach ($vars as $v) 
	{	
		$v=trim($v); if (!empty($v)) return $v;
	}
	return false;
}

	
// dump($var, $isHTML=true)	 - выводит (или возвращает) дамп переменной, без использования буферизации вывода   
// может работать внутри обрабочика ob_start
// Параметр $isHTML (по умолчанию true) влияет на представление переносов строк ("<BR>" или "\n") и пробелов ("&nbsp;" или " ")
// TODO: http://ru2.php.net/%20print_r - хорошие примеры для определения рекурсс, расцветки и построения дерева
function dump($var, $isReturn=false, $level=0)
{
    static $counter=0;
	$hidden_level = 1;
	
	if($level>6 ) return '... ';
	$isHTML=1;
	$SPACE 	= $isHTML ? "&nbsp;" : " ";
	$SPACE 	= " ";
	$TAB	= str_repeat($SPACE, 3);
	$PREFIX = str_repeat($TAB, $level);
	$NL 	= ($isHTML ? "<BR>" 	 : "\n") .$PREFIX;
	
	$result = '';
	
	if (is_array($var) || is_object($var)) 
	{
        
		$link_text = 'array';
		if (is_object($var))  {
			$var = (array)$var; $link_text = 'object' ;
			}
		$span_id = 'dump_'.++$counter;
		
		$count = count($var);
		
		if ($count)
			$result .= "<a class=dump_dashed href='javascript:dump_toggle(\"{$span_id}_mark\"); dump_toggle(\"$span_id\");' onclick='this.blur();' >$link_text</a>";
		else 
			$result .= $link_text;
		
		$result .= "<sup>".$count."</sup>";
		
		
		$style = $level>=$hidden_level && count($var)  ? " style='display:none' " : "";
		$style_mark = $style? "" : " style='display:none' ";
		
		$result .= "<span id={$span_id}_mark  $style_mark >(...)</span><span id=$span_id  $style >(";
		
			foreach ($var as $key => $value) 
			{
				$result .= 	$NL.$TAB;
				if ($key === 'GLOBALS') 
					$result.= '[GLOBALS] => *** Recursive Array ***';
				else 
					$result.= "[$key] => "
								.dump($value, 1, $level+1);
				$result .= ',';
			}
			if (count($var))  
				$result = rtrim($result, ',') . $NL;
		$result .= ')</span>';
    } 
	elseif (is_string($var))
	{
		$len = strlen($var); 
		$var = "'".htmlspecialchars($var, ENT_QUOTES)."'";
		
		if ($len<=1000) $result .= $var;
		else
		{
			$span_id = 'dump_'.++$counter;
			
			$result .= "<a href='javascript:dump_toggle(\"{$span_id}_mark\"); dump_toggle(\"$span_id\");' onclick='this.blur();' 
				class=dump_dashed
				>(string)</a><sup>$len</sup><span id={$span_id}_mark >... </span><span id=$span_id  style='display:none' >$var</span>";	
				
		}
		
    }
	elseif (is_bool($var))	$result .= $var ? 'TRUE' : 'FALSE';
    elseif (is_null($var))	$result .= 'NULL';
    else $result .= $var;    
    
	if ($isHTML && !$level)
	{	
		$result = "
			<style>
			PRE.dump {
				 margin:0;
			}
			pre {
				white-space: pre-wrap;       /* css-3 */
				white-space: -moz-pre-wrap;  /* Mozilla, начиная с 1999 года */
				white-space: -pre-wrap;      /* Opera 4-6 */
				white-space: -o-pre-wrap;    /* Opera 7 */
				word-wrap: break-word;       /* Internet Explorer 5.5+ */
			}
			.dump A {
				color:#2545BE;	
				white-space:nowrap;
			}
			.dump A:hover {
				color:red;
				text-decoration:none;
			}
			
			.dump_dashed {
				border-bottom: 1px dashed #000;
				}
			A.dump_dashed {
				border-bottom: 1px dashed #2545BE;	
				text-decoration:none;
				}
			A.dump_dashed:hover {
				border-bottom: 0;	
				text-decoration:none;
				}
			
			.dump SUP { 
				color:#999;
				}
			</style>
			<script>
			function dump_toggle(id){
				var s= document.getElementById(id).style
				s.display= s.display=='none' ? s.display='' : 'none'; 
			}
			</script>
			<pre class=dump >$result</pre>";
	}
	if ($isReturn) return $result;
	else print $result;
}	

// аналог dump() - но по умолчанию возвращает HTML-дамп строкой	
function get_dump($var)
{
	return dump($var, $isReturn=true);
}


// заменяет в шаблоне $template все входения %key% на $value (где key - это ключи в массиве $vars, а $value - значения в этом массиве) 	
function vars_replace($vars, $template)
{
	$keys=array_keys($vars);
	foreach ($keys as &$key) 
		$key = '%'.trim($key,'%').'%'; 
	
	return str_replace( $keys, $vars, $template);
}

// подгатавливает строку для вывода в составе HTML страницы
function html($str)
{
	return htmlspecialchars($str, ENT_QUOTES);
}

// подгатавливает строку для вывода через JavaScript alert()
function html_alert($str)
{
	return 	str_replaces(strip_tags($str), array(
				
				"\n"	=> '\n',
				'"'		=> '\\"',
				"'"		=> "\\'",
	
			));
}


// возвращает URL адрес, соответствующий серверному пути к файлу
function path2url($path)
{
	// вычисляем корневую директорию сайта
	// $site_root = getenv("DOCUMENT_ROOT");
		// if(!$site_root) $site_root = $_SERVER['DOCUMENT_ROOT'];
		// $site_root = str_replace('\\','/', $site_root); 
	
	// нормализуем текущую директорию и вычитаем из ней префикс, равный корневой директории сайта 
	
	$SITE_ROOT = SITE_ROOT;
	$SYSTEM_ROOT = SYSTEM_ROOT;
	
	return str_replace(
		array('^^'.$SITE_ROOT,  '^^'.$SYSTEM_ROOT, '^^'), 
		'', 
		'^^'.str_replace('\\','/', $path));;
}



/******/





 
 
 /*
 * Undo magic quotes
   http://php.net/manual/en/security.magicquotes.disabling.php.
 */
function undo_magic_quotes()
{
    ini_set('magic_quotes_sybase', 0);
    ini_set('magic_quotes_runtime', 0);
    
    if (function_exists('get_magic_quotes_gpc') 
        && get_magic_quotes_gpc() === 1
        ) 
    {
        $_POST    = array_map('stripslashes_deep', $_POST);
        $_GET     = array_map('stripslashes_deep', $_GET);
        $_COOKIE  = array_map('stripslashes_deep', $_COOKIE);
        $_REQUEST = array_map('stripslashes_deep', $_REQUEST);
    }
}
    
function stripslashes_deep($value) 
{
    $value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
    return $value;
}
 
 
// return filename extention
function file_ext($filename)
{
	//print_r($filename); echo "| ";
	return strtolower(substr(strrchr(basename($filename), '.'),1));
}
//
// pr(file_ext('text.txt'));


			
			
			
			

// return filename without extention
function file_wo_ext($filename)
{
    if (FALSE===strrpos($filename,'.')) 
		return $filename;
	else	
		return substr($filename,0,strrpos($filename,'.'));
}
 

 /** 
 * Get the directory size 
 * @param directory $directory 
 * @return integer 
 */ 
function dir_size($directory) { 
    $size = 0; 
    foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory)) as $file){ 
        $size+=$file->getSize(); 
    } 
    return $size; 
} 


/** 
 * Converts human readable file size (e.g. 10 MB, 200.20 GB) into bytes. 
 * 
 * @param string $str 
 * @return int the result is in bytes 
 * @author Svetoslav Marinov 
 * @author http://slavi.biz 
 */ 
function filesize2bytes($str) { 
    $bytes = 0; 

    $bytes_array = array( 
        'B' => 1, 
        'KB' => 1024, 
        'MB' => 1024 * 1024, 
        'GB' => 1024 * 1024 * 1024, 
        'TB' => 1024 * 1024 * 1024 * 1024, 
        'PB' => 1024 * 1024 * 1024 * 1024 * 1024, 
    ); 

    $bytes = floatval($str); 

    if (preg_match('#([KMGTP]?B)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) { 
        $bytes *= $bytes_array[$matches[1]]; 
    } 

    $bytes = intval(round($bytes, 2)); 

    return $bytes; 
} 

// a quick way to convert bytes to a more readable format 

function format_bytes($bytes, $precision = 2) 
{ 
    $units = array('B', 'KB', 'MB', 'GB', 'TB'); 
   
    $bytes = max($bytes, 0); 
    $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); 
    $pow = min($pow, count($units) - 1); 
   
    $bytes /= pow(1024, $pow); 
   
    return round($bytes, $precision) . ' ' . $units[$pow]; 
} 

function is_image($fn) 
{
	return in_array(file_ext($fn), array('jpg', 'jpeg', 'png', 'gif'));
}

 
 
/** 
 *  save_array($filename, $data_array) - записывает содержимое многомерного массива в файл в виде php кода
 */
function save_array($filename, $data_array)
{
    file_put_contents( $filename.'~', 
        "\xEF\xBB\xBF". // Записываем в начале 3 байта (utf-8 BOM - byte order mark) для указания кодировки контента как UTF-8, без этого документ распознается в редакторах как ANSI
		"<? return \n". var_export($data_array, true).";\n?>"  
        );
    @unlink($filename);
    rename($filename.'~', $filename);
    chmod($filename, 0666);
    // очищаем кеш для измененного файла (без чтения файла)
    read_array($filename,$clearcache=true);    
     
}


/** 
 *  read_array($filename, $clearcache=false) - читаем массив из файла
 *      Если установлен $clearcache - то просто очищает кеш для файла
 */
function read_array($filename, $clearcache=false)
{
    static $cache = array(); 
    if ($clearcache)
    {
        if($filename) unset($cache[$filename]);
        else $cache=array();
        return;
    }
    if (!empty($cache[$filename])) return $cache[$filename];    
    
    if (file_exists($filename))
	{	
        ob_start(); // защищаемся от вывода в браузер UTF-8 BOM байтов 
		$cache[$filename] = (array) include($filename);
		ob_end_clean();
		
		return $cache[$filename];
    }
	else 
        return array();
    
}
 
 
 

/** rglob($pattern='*', $flags = 0, $path='') - recursive glob()
 * @param int $pattern
 *  the pattern passed to glob()
 * @param int $flags
 *  the flags passed to glob()
 * @param string $path
 *  the path to scan
 * @return mixed
 *  an array of files in the given path matching the pattern.

	Example usage:
	chdir('../');
	var_export(rglob('*.php'));

*/

function rglob($pattern='*', $flags = 0, $path='')
{
    $paths=glob($path.'*', GLOB_MARK|GLOB_ONLYDIR|GLOB_NOSORT);
    $files=glob($path.$pattern, $flags);
    foreach ($paths as $path) { $files=array_merge($files,rglob($pattern, $flags, $path)); }
    return $files;
}






// возвращает URL адрес, соответствующий серверному пути к директории
// $path - это серверный путь к директории или к файлу в этой директории
function dir_url($path)
{
	return path2url(dirname($path));
}

// заменяет в URL значения указанных переменных на новые 
function url_merge($query_vars, $url=null)
{
	if (!$url) $url = $_SERVER['REQUEST_URI'];
	if (!strstr($url,'?') && strstr($url,'&')) $url = '?'.$url;
	list($path, $query) = explode('?', $url.'?');
	
	parse_str($query, $query);
	if (!is_array($query_vars)) parse_str($query_vars, $query_vars);
	// pr($query, $query_vars);
	
	$query = array_merge($query, $query_vars);
	// pr($query);
	
	$query_new = ''; 
		foreach ($query as $var => $val)
			if($val)
				$query_new .= "&$var=$val";
		
	$url_new = 	$path;
	if ($query_new) $url_new.= '?'.trim($query_new, '&');
	
	return $url_new;
}
// pr(url_merge(null, 'subfolder=folder&pg='));




// рекурсивное копирование директории - вместе со всеми поддиректориями и файлами 
function copy_dir($src,$dst) 
{ 
    $dir = opendir($src); 
    @mkdir($dst); 
	
    while(false !== ( $file = readdir($dir))) 
	{ 
        if (( $file != '.' ) && ( $file != '..' )) 
		{ 
            if ( is_dir( $src .'/'. $file))  
                copy_dir($src .'/'. $file,$dst .'/'. $file); 
            else  
                copy( $src .'/'.$file, $dst.'/'. $file); 
        } 
    } 
    closedir($dir); 
} 




//array of files without directories... optionally filtered by extension 
function file_list($dir, $filter='', $wo_ext=false, $show_hidden=false)
{ 
    $list=array();
	
	foreach(array_diff(scandir($dir),array('.','..')) as $file)
	{
        if ($file[0]=='.' && !$show_hidden ) continue;
		if ($filter && !strstr($file, $filter)) continue;  //(($x)?ereg($x.'$',$file):1))
		if(!is_file($dir.'/'.$file)) continue;
        if ($wo_ext) $file = file_wo_ext($file);
		$list[]=$file; 
    }
	sort($list, SORT_REGULAR);
	return $list; 
} 
//pr(file_list(FILES_DIR)); //$dir, $filter='', $show_hidden=false)


//array of directories (recursive = and subdirectories)
function dir_list($dir, $recursive=false, $show_hidden=false, $prefix='')
{
	static $cache=array();
	$hash= md5(serialize(func_get_args()));
	if(isset($cache[$hash])) return $cache[$hash];
	
	$l = array();
	
    $prefix=trim($prefix,'/ ');
	
	if (is_dir($dir))
	foreach(array_diff(scandir($dir),array('.','..')) as $folder)
	{
        if(is_dir($dir.'/'.$folder) )
		{
            if ($folder[0]=='.' && !$show_hidden) continue;
			
			$l[]=($prefix? "$prefix/" : "") . $folder; 
			if ($recursive 
				&& $sublist=dir_list("$dir/$folder", true, $show_hidden, "$prefix/$folder")
				)
				{	
					
					$l= array_merge($l,$sublist); 
				}
		}	
    }
	return ($cache[$hash] = $l); 
}

//pr( dir_list(FILES_DIR, $recursive=true));


/** 
 * find_file_exists($file1, file2,... ) - ищет первый существующий файл
 *
 * @param  string/array $file1, file2,...  -  полные пути к файлам, любое число аргументов / либо массивы (массивы) со списком файлов
 * Если следующий аргумент после имени файла = true , то им файла раскрывается в полный путь
 
 * @return string - возвращает первый найденный существующий файл, либо false
 */
function find_file_exists() 
{
	$files= func_get_args();
	$SITE_ROOT = $_SERVER['DOCUMENT_ROOT'];
	$SYSTEM_ROOT = SYSTEM_ROOT;
	
	// pr_($files);
	
    for ($i=0,$count=count($files); $i<$count; $i++ )
    {
        $file = $files[$i];
		// pr_($file);
		
		
		if (true===$file) continue;
		
		$expand_path = val($files[$i+1])===true ? true : false;
		
		
        if (is_array($file))
		{
			if ($result = call_user_func_array(__FUNCTION__, $file))
				return $result;
		}	
        else
		{	// pr($file);
				
				if (!($file=trim($file))) continue;
				
				static $CHECKED_FILES = array();
				if (isset($CHECKED_FILES[$file]))
				{
					if ($CHECKED_FILES[$file]) return $CHECKED_FILES[$file];
					else continue;
				}
				
				// if(strstr($file , 'posts'))
				// pr(file_exists($f = $file));
				// pr_($file);
				
				if(strstr($file, $SITE_ROOT) OR strstr($file, $SYSTEM_ROOT))
					if ( file_exists($f = $file)
						 OR ($f = find_file_nocase($file))
						)  
						return  ($CHECKED_FILES[$file] = $f);
					
				// возможно, это не абсолютный путь, а URL к файлу
				
				if (	file_exists($f=$SITE_ROOT.$file)
						OR	file_exists($f=$SYSTEM_ROOT.$file)
					)
					{
						$file = $expand_path ? $f:$file; 
						return ($CHECKED_FILES[$file] = $file);
					}
					
				/* if ( 	($f = find_file_nocase($SITE_ROOT.$file))
					 OR	($f = find_file_nocase($SYSTEM_ROOT.$file)) 
					
					) return str_replace(array($SITE_ROOT,$SYSTEM_ROOT),'', $file);
				 */
		}
	}    
	// pr($result);
	
    return false;
}


// проверяет, существует ли файл, независмо от регистра в имен файла
// возвращает корректное имя файла. 
function find_file_nocase($path) 
{  
    // echo $path."<br/>";
	
	$dirname = dirname($path);
    $filename = strtolower(basename($path));
	
	if(!is_dir($dirname)) return false; 
	
	
	// проверяем стандартные варианты имени файлов 
	$path_varians = array(
		$path, 
		$dirname .'/'.ucfirst($filename),
		$dirname .'/'.strtolower($filename),
		$dirname .'/'.strtoupper($filename),
		);
	
	foreach ($path_varians as $path_var)
		if (file_exists($path_var ))
			{return $path_var;}

	/*
	// если не найдено, сканируем весь каталог 
    $dir = dir($dirname);
    while ( ($file = $dir->read()) !== false) {
        if (strtolower($file) == strtolower($filename)) {
            $dir->close();
            return  $dirname .'/'.$file;
        }
    }
    $dir->close();
	*/
	
	
    return false;
}

// создает папку и если не сущесвтуют над-папки - то и их тоже 
function mkdir_recursive($path, $perms=0777)
{
	$path = str_replace('/', DIRECTORY_SEPARATOR , $path);
	// pr($path);
	mkdir($path, $perms, true);
}







// ищет в $haystack самое раннее вхождение любой из строк переданных в массиве $needles, начиная с позиции $offset
function multi_strpos($haystack ,$needles=array(),$offset=0)
{
    $res = array();
    foreach($needles as $needle){
        if (strpos($haystack,$needle,$offset) !== false) {
          $res[] = strpos($haystack,$needle,$offset);
       }
    }
    if(empty($res)) return false;
    return min($res);
}




 
/**
 * Sanitize a variable, to make it ready for saving in a file.
 *
 * @param string $var The variable to sanitize.
 * @param boolean $html Should it convert HTML too?
 * @return string The sanitized variable.
 */
/* function sanitize($var, $html = true) {
	$var = str_replace('\'', '\\\'', $var);

	if ($html == true)
		$var = htmlspecialchars($var, ENT_COMPAT, 'UTF-8', false);

	return $var;
}
 */
 
/**
 * Convert a given string to a SEO safe URL, simbols: a-z_/ (and '.' if it is filname)
 *
 * @param string $url String for converting to a SEO URL.
 * @return string A SEO safe URL.
 */
function seo_url($url, $is_filename=false) {
	require (CORE_DIR.'/lib/url_replace.php');

	$url = preg_replace('/\s+/', '_', $url);
	foreach ($lang_url_replace as $old => $new)
	{	
        $url = str_replace($old, $new, $url);
    }
	$url = strtolower(urlencode($url));
	
    $url = preg_replace('/(%[0-9a-f][0-9a-f])/', ' ', $url);
    $url = preg_replace('/[\-\_\s]+/', '_', $url);
	if (!$is_filename)
        $url = preg_replace('/\./', '_', $url);
    
    $url = trim($url, '-_ ');
    
    if (!$url || $url=='.' || $url=='..' ) $url='_';
	return $url;
}



function redirect301($url)
{
	redirect($url,0,301);
}

//pr($_SERVER);

/* 
redirect($url,$time = 0, $status=302)
	осуществляет редирект с помощью PHP или HTML/JS (если заголвоки уже отправлены или нужна задержка) 
Параметры:
	$url - URL с хостом или без
	$time - время задержки в секундах или флаг возврата. Если $time < 0, то делается попытка вернуться в предыдущей странице ($_SERVER['HTTP_REFERER'])
	$status - HTTP-код отклика сервера (301,302, 307, и т.п.)
*/	
function redirect($url='',$time = 0, $status=302)
{
	// время преобразуем к целым секундам
	//$time = intval($time);
	//pr($url);
	
	//if ($time < 0 && $rurl=Core::get_return_url()) $url=$rurl; 
	
	// URL приводим к безопасному и пригодному виду 
	if(!$url) $url=$_SERVER['REQUEST_URI'];
	$parsed_url = parse_url($url);
	if (!stristr($url,$_SERVER['HTTP_HOST']) && !isset($parsed_url['host']))
		$url = $_SERVER['HTTP_HOST'] .'/'.  ltrim($url,'/');
	
	if (!isset($parsed_url['scheme']))
		$url = 'http://' . $url;
	
	
	
	/* FIX:  это некорректно работает с URL вида		
			/admin/files/?filter_name=&subfolder=baners%2Ftest
			(%2F - в параметрах)

	$url = urlencode($url);
	$url = str_replace(
		array('%3F','%3D','%26','%2F','%3A','','',''), 
		array('?','=','&','/',':','','','',''), 
		$url);
	*/	
	// pr($time);
	// pr((int)headers_sent());
	//pr($url);
	
	// если заголовки ещё не отправлены - делаем HTTP-редирект
	if (!headers_sent() && !$time) 
	{    //If headers not sent yet... and noneed timeout then do php redirect
        header($str='Location: '.$url, true,  $status); 
		//pr($str);
    }
	else // если HTTP-заголовки уже отправлены - делаем редирект с помощью HTML или JavaScript
	{  
        echo '
		<script type="text/javascript">
		setTimeout(\'location.replace("'.$url.'")\', '.$time.'000);
		</script>
        <noscript>
		<meta http-equiv="refresh" content="'.$time.';url='.$url.'" />
		</noscript>
		';
		
		
    }
	
	// если задержка не нужна, то завешаем выполнния скрипта
	if (!$time) exit;
}


// редиректит на возвратный URL (переданный через GET / POST / COOKIE), а если такого нет, то на указанный дефолтный  URL
/* function redirect_return($default_url, $time=0)
{
	redirect(return_url($default_url), $time);
}

function return_url($default_url='')
{
	($url = GET('return_url'))
	OR ($url = POST('return_url'))
	OR ($url = $default_url);	
	//OR ($url = COOKIE('return_url'))
	//OR ($url = $_SERVER['HTTP_REFERER'] )
	
	return $url;
} */

// возрващает полный url текущей старницы
function page_url() 
{
	return 	'http'
			.(!empty($_SERVER["HTTPS"]) ? 's' :'')
			.'://'
			.$_SERVER["SERVER_NAME"]
			($_SERVER["SERVER_PORT"] != "80"  ?  ":".$_SERVER["SERVER_PORT"] : '' )	
			.$_SERVER["REQUEST_URI"]
			;
}


// pr() - выводит дампа переменных с остановом
function pr()
{
	$args=func_get_args();
	
	if ($args[0]!==null )
		while (ob_get_level()) {ob_end_clean();}
	
	call_user_func_array('pr_',$args);
	exit;
}
// pr() - выводит дампа переменных без останова
function pr_() 
{
	// получаем информацию о вызывающей строке
	$info_all = debug_backtrace();
	foreach ($info_all as $info)
		if (isset($info['file'])
			&& $info['function']!='call_user_func_array'
			) break;
	// echo"<pre>";print_r($info);
	
	// выводим всю информацию о вызывающей строке
	echo 	'<TABLE ><TR>
			<TD style="background:#F8FA8E; padding:3px 6px; height:25px;"  >  
			<b2>Файл</b>: "<b title="'.$info['file'].'">' 
			. basename($info['file'])
			.'</b>"'
			.', <b2>строка</b> <b>'.$info['line'] .'</b> '
			.', время скрипта: <b>'.script_time() .' </b> '
			.'<TR><TD style="background:#eee; padding:5px; font: normal  17px monospace;"><b>'
			.$info['line'].':</b> '
			.( ($f=file($info['file']))? html($f[$info['line']-1]) : '')
			
			.'</TABLE>'
			;

   // обеспечиваем кроссбраузерный перенос длинных строк в теге <PRE>
   print "<style>
	pre {
		white-space: pre-wrap;       /* css-3 */
		white-space: -moz-pre-wrap;  /* Mozilla, начиная с 1999 года */
		white-space: -pre-wrap;      /* Opera 4-6 */
		white-space: -o-pre-wrap;    /* Opera 7 */
		word-wrap: break-word;       /* Internet Explorer 5.5+ */
	}
	</style>
    ";
   
   // выводим все аргументы функции
   $args=func_get_args();
   foreach ($args AS $var)
   {
        if($var==null) continue;
        print "<pre>\n"
              .htmlspecialchars(@print_r($var, 1))
              ."\n</pre>";
        
   }
   
}



// возвращает интервал в милисекундах от последнего вызова script_time(true) (если таковой был) или от самого первого вызова script_time()
script_time();
function script_time($is_reset=false)
{
	static $START_TIME=0;
	
	$this_time = microtime(true);
	$interval =  $START_TIME? round(1000*($this_time-$START_TIME),2) : 0;
	
	if(!$START_TIME OR $is_reset) $START_TIME = $this_time;
	return $interval.'ms';
}





// extract 1st <img> src from post content with image
function extract_image($content)
{
    if (!stristr($content,'<img')) return '';
    
    if (preg_match('/<img [^>]*src\s*=\s*["\']?([^"\' ]+)["\' >]/is',$content, $matches))
        return $matches[1];
    
}

// extract all <img> src from post content to array
function extract_images($content)
{
    if (!stristr($content,'<img')) return '';
    
    if (preg_match_all('/<img [^>]*src\s*=\s*["\']?([^"\' ]+)["\' >]/is',$content, $matches))
    {    
		//pr($matches);
		
		return $matches[1];
    }
  
    
}


// extract_anons($content, $minlen=null, $maxlen=null, $allow_tags='')
//      Extract anons from $content, strip tags and cut at <hr> or at length params
// @param $minlen - min length of anons by chars
// @param $maxlen - max length of anons by chars
//      It make end of anons at the end of sentenses or word
// @$allow_tags - allowed tag in anons, example: '<a><b><p>'
// $param $modde - default 'before' - return anons, 'after' - return string after anons, 'both' - return both string in array
function extract_anons($content, $minlen=null, $maxlen=null, $allow_tags='',$mode='before')
{
    if (!$allow_tags) $allow_tags = '<sup>';
    
    $content_full = $content;
    
	if(!$maxlen) $maxlen=350;
    if(!$minlen) $minlen=(int)($maxlen/2);
    
    
    if ($hr = strpos($content,'<hr'))
    {
        
        $content1= strip_tags(substr($content_full, 0,$hr),$allow_tags);
        $content2= strip_tags(substr($content_full,$hr),$allow_tags);
        
    }    
    else
    {   
        
        // cut content according $maxlen param
        $content= $content_full = strip_tags($content,$allow_tags);
        
        $content= mb_substr(str_replace('&nbsp;',' ',$content),0,$maxlen+1,'utf-8');
        
        
        
		
		// find last sentences delimiter OR last word delimiter
		if( preg_match('/([\.\?\!])/', strrev($content), $match1, PREG_OFFSET_CAPTURE )
			|| preg_match('/([\s,:;])/', strrev($content), $match2, PREG_OFFSET_CAPTURE ))
        {
            $last_delim1 = strlen($content)-@$match1[0][1];
            $last_delim2 = strlen($content)-@$match2[0][1]-1;
			
			$last_delim = strlen($content);
			if ($last_delim1>=$minlen)  $last_delim=$last_delim1;
			else if ($last_delim2>=$minlen)  $last_delim=$last_delim2;
			
            
            // extract string from start to last delimiter
            $content1= substr($content,0,$last_delim);
            $content2= trim(substr($content_full,$last_delim));
        }
        else {$content1=$content2=$content;}
        
        // FIX ME: its need to close non closed HTML tags
        
        //pr("'$content'");
    }
    
    $content1 = trim($content1);
    $content2 = trim($content2);
    
    if (!$mode || $mode=='before') return $content1;
    if ($mode=='after') return $content2;
    if ($mode=='both')  return array($content1,$content2);    
    
}






// обрезает строку и добавляет троеточие
function str_limit($str, $maxlength = 256, $continue = "\xe2\x80\xa6", &$is_cutted = false) 
{
    $str = strip_tags($str);
	if (mb_strlen($str) <= $maxlength) return $str;
	$str=mb_substr($str,0,$maxlength) . $continue;
    $is_cutted = true;
    return $str;
}
//pr(str_limit('123456789012345', 10));
// pr(str_limit('абвгabcdefghiklmnop', 10));
// pr(str_limit('абвгдеёжзиклмн', 10));



/**
 * short_text($content, $size=200, $allow_tags='') - создает краткий анонс из текста статьи.  Пытается выделить в анонс текст, завершаемый разделителем предложений (!?.) или разделителем слов (пробел,;:)   
 *
 *
 */
 
function short_text($content, $size=200, $allow_tags='')
{
    //pr($content);
	
	//$allowed_tags = "<p><b><i><u><ul><ol><span><a><br><div><font><br/><table>";
	$allow_tags .= '<sup>'; // в анонсе сохраняем некоторое форматирование 
    
	// устанавливаем примелимый предел для поиска разделителей слов и предложений
	$maxlen = $size+25;
	$minlen= max(50, $size-50);
        
	// подготавливаем максимальный по размеру анонс, без учета разделителей слов 
	
	// обеспечиваем пробелы в местах переноса строк
	$content = str_replace(
		array('&nbsp;'	, '<br'	,'<BR'	,'</p>'	,'</P>'),
		array(' '		, ' <br',' <BR'	,' </p>',' </P> '),
		$content
		);
	$content = strip_tags($content,$allow_tags);
	$content = mb_trim(mb_substr($content, 0, $maxlen+1));
    //
	// pr($size, $content );   
	if(empty($content)) return $content;
    // if(strlen($content)<$minlen) return $content;
	
	// Теперь ищем подходящий разделитель предложений
    $end = max(	mb_strrpos( $content , '!')+1,
				mb_strrpos( $content , '?')+1,
				mb_strrpos( $content , '. ')+1
				);
    // pr($end);
	
	// если не найден (в приемлимом диапазоне), ищем раделитель слов 
	if ($end < $minlen)
	$end = max(	mb_strrpos( $content , ' ')
				,mb_strrpos( $content , ':')
				,mb_strrpos( $content , ';')
				,mb_strrpos( $content , ',')
				,mb_strrpos( $content , "\n")
				
				);	
	// pr($content,$end); 
	
	// если не найден - принудительно обрежем контент по заданному size
	if ($end < $minlen)
		$end = $size;
		
	//pr($end); 	

	$content = mb_substr($content,0,$end);
    
	$content =  close_tags($content, $allow_tags); 
	
    //pr("'$content'");
   
	return $content;    
}

/**
 *  close_tags($code, $tags) - добавляет закрывающие теги, если в коде есть незакрытые теги из указанного списка. 
 *  Пример: 
		$content =  close_tags($content, '<a><b><li><font>')
		$content =  close_tags($content, 'a b li font')
 */
function close_tags($code, $tags)
{
	$tags = str_replace(array('<', '>', ',', '    ','  '),' ', $tags);
	$tags = explode(' ',$tags);
	foreach ($tags as $tag)
	{
		if (!trim($tag)) continue;
		$opened_count = count(spliti('<'.$tag,  $code));
		$closed_count = count(spliti('</'.$tag, $code));
		
		if ($opened_count > $closed_count) 
			$code .= str_repeat( "</$tag>", $opened_count - $closed_count );
	}

  return $code;
 }


 

 

/***
eval_phtml($phtml, $vars_array=array()) 
    - интерпретиирует код HTML+PHP и возвращает код HTML

@param $phtml   - html код , который моежт содержать вставки php кода. 
@param $vars_array - ассоциативный массив, занчение которого импортируются как переменные в область видимости кода $phtml. Если элементы массива передаются как ссылки, то код в $phtml может их изменять.

Пример 1: 
    $template= '<h1><?=$h1?></h1> <?if($text){?> <p><?=$text?></p> <?}?>';
    $h1      = 'Какой-то заголовок';
    $text    = 'Какой-то текст';
    print $content = eval_phtml($template, get_defined_vars());
    
    // вывод: 
    // <h1>Какой-то заголовок</h1> <p>Какой-то текст</p>

Пример 2:    
    $content = eval_phtml($template, array('h1'=>'Заголовок', 'text'=>'текст'));
    
    // вывод: 
    // <h1>Заголовок</h1> <p>текст</p>
    
Пример 3:    
    (использование передачи переменных-ссылок для возможности записи в переменные)
    
    $x=1;
    echo "старое значение: \$x=$x  <br>";
    
    $phtml='<? $x=2; ?>';
    eval_phtml($phtml, array(x=>&$x));
    echo "новое значение: \$x=$x  <br>";
    
    // вывод:
    // старое значение: $x=1  <br>
    // новое значение: $x=2   <br>

*/
function eval_phtml($eval_phtml_code, $arguments=array())
{
	if(FALSE === strpos($eval_phtml_code,'<?')) return $eval_phtml_code;
    if ($arguments && is_array($arguments)) 
    {
        extract($arguments, EXTR_REFS);
        //if($element) extract($element);
    }
    ob_start();
	//
	@$eval_phtml_success=eval("?>$eval_phtml_code<? return 1;"); 
	$eval_phtml_result=ob_get_contents(); 
	ob_end_clean();
    
    // отлавливаем ошибки в коде
    //pr($eval_phtml_success);
	
    if (!$eval_phtml_success && defined('DEBUG') && DEBUG) 
    {
        // если нужно, здесь переводим сообщения об ошибках
        // $eval_phtml_result=error_translate($result);
        
        echo  $eval_phtml_result; // выводим сообщение об ошибке
        print_code($eval_phtml_code); // выводим  нумерованый код
        exit;
    }
    return $eval_phtml_result;
}	

// рекурсивно обрабатывает элементы массива, производя в них замену строк и интерпретацию php кода
function  array_replace_eval( &$arr, $replace_list, $vars=array())
{
	//echo "$arr->id | ";
	//if($arr->id=='blog') pr($arr);
	
	/* $code = serialize($arr);
	if (false===strpos($code,'<?') 
		&& false===strpos($code,'%')
		) return;  */
	
	foreach ($arr as &$item)
	{
		if (is_array($item))  
			array_replace_eval( $item, $replace_list, $vars);
		else
		{
			$item = str_replace ( array_keys($replace_list) , $replace_list, $item);
			if (false!==strpos($item,'<?'))
				$item = eval_phtml($item, $vars);
		}
	}
	unset($item);
}


// заменяет в строке $str все ключи массива  $array_replaces на соответствующие значения из этого массива
function str_replaces($str, $array_replaces)
{
	// преобразуем объект к массиву 
	$array_replaces = (array)$array_replaces;
	
	$result =  str_replace ( 
				array_keys($array_replaces) , 
				$array_replaces, 
				$str
				);
	
	return $result;
	
}

// заменяет в строке $str все ключи массива $array_replaces в форме %key% (т.е. с добавлением % по краям) на соответствующие значения из этого массива, вместо массива может быть объект, и он будет преобразован в массив 
function macro_replaces($str, $array_replaces)
{
	$array_replaces = (array)$array_replaces;
	
	$macro_replaces = array();
		foreach ($array_replaces as $key=>$val)
			$macro_replaces["%$key%"] = $val;
	
	return 	str_replaces($str, $macro_replaces);	
}




/* array_sortby(&$data, $sortby) - сортирует массив ассоциативных массивов (или массив стандартных объектов) по одному или нескольким свойствам, по возрастанию (ASC) или убыванию (DESC). 

Функция ничего не возвращает, - первый аргумент (массив) передается по ссылке и сортируется.  
 
Примеры:
    
	array_sortby($data, 'order, title, price DESC')

    $data= Blog::get_posts();
    array_sortby($data, 'show DESC, id ASC, title');
    pr($data);

*/
function array_sortby(&$data, $sortby)
{
    //pr(is_array($data));
    
    static $sort_funcs = array(); // init cache for sort functions
    if (!$sortby || !$data || !is_array($data)) return;
  
  //pr($data);
  
   // check cache for existing sort_func
    if (!empty($sort_funcs[$sortby])) 
        $sort_func = $sort_funcs[$sortby];

        // create sort_func, if no sort_func in cache
    else 
    {
       $code = "\$c=0; \n"; // start code of sorting functions
       $code.= "\$a=(array)\$a_arg; \$b=(array)\$b_arg; \n";// objects-arg to arrays
	    
	   //pr(split(',', $sortby));
       
	   // пройдемся по каждому поля для сортировки и 
	   // сформируем код строки функции для сравнения элементов массива
	   foreach (split(',', $sortby) as $field_order) 
       {
            // check for empty string
            if (!trim($field_order)) continue;
            
            // extract fieldname and fieldorder (ASC or DESC)
            $field_order = trim(str_replace(array('  ','   ','     '),' ',$field_order));
            @list ($fieldname, $order) = split(' ', $field_order.' ');
            //echo "fieldname='$fieldname', order='$order' <br>\n";
            
            
            if ($order != 'DESC') $order='ASC';

            // find the row of array for testing types of row-fields
            foreach ($data as $item)
				if (($item = (array)$item) && isset($item[$fieldname])) break;
			if (!isset($item[$fieldname])) continue;
			
             
            //pr($item);
            //pr($item[$fieldname]);
            
            // generate right expression for sorting
            
            // if nums - eval differens
            if(is_numeric($item[$fieldname])) 
                $code.= "\$c = (@\$a['$fieldname'] - @\$b['$fieldname']); \n";
                
            // if no nums (string) - call comparsion func 
            else                       
                $code.= "\$c = strcasecmp(@\$a['$fieldname'],@\$b['$fieldname']); \n";
            // invers result, if this field need DESC sorting    
            if ($order == 'DESC') 
                $code.= "\$c = -\$c; \n";
            
            // return $c, if there is difference
             $code.= "if(\$c) return \$c; \n";
       
       }
       // return zero, if no difference of all fields
       $code .= 'return $c;';
       //pr($code);
       
       
       // add sortfunc to cache
       $sort_func = $sort_funcs[$sortby] = create_function('$a_arg, $b_arg', $code);
       
    } 
    //pr($data);
    
    // sort array
    $sort_func = $sort_funcs[$sortby];
    uasort($data, $sort_func);
    //pr($sort_func);
}



/*
array_merge_recursive_distinct(&$a1, &$a2)

    merge 2 arrays , and overwrite existing keys in array1 by key=>value of array2

*/
function array_merge_recursive_distinct ( array &$array1, array &$array2 )
{
  $merged = $array1;

    foreach ( $array2 as $key => &$value )
    {
        if( is_array ($array2[$key])  
            && isset ($merged[$key]) 
            && is_array ($merged [$key]) 
           )
        {
            $merged [$key] = array_merge_recursive_distinct ($merged[$key], $array2[$key]);
        }
        else
        {
            $merged[$key] = $value;
        }
    }

    return $merged;
}

function object_merge($obj1,$obj2)
{
	return (object)array_merge((array)$obj1, (array)$obj2);
}


/**
 * eval_template($template, $data) - исполняет файл шаблона $template и вставляет в него содержимое массива $data 
 *
 * @param string $template - путь к файлу шаблона
 * @param string/array $data  - данные для вставки в шаблон. 
 *      Если $data - строка, то данные вставляются в переменную $content. 
 *      Если $data - одномерный ассоциативный массив, то данные вставляются в  переменые, соответсвтующие ключам массива.  
 *      Если $data - двумерный массив, то в шаблон передается переменная $data без изменений, и шаблон должен будет обработать $data в цикле.
 *
 */
function eval_template($template_path, $data=array(), $additional_vars=array())
{
    // echo "$template_path<br/> ";
	
    //if (!$data) return '';// это должен определять сам шаблон
    
    // если $data - строка, назначаем строку в переменную $content   
    if (is_string($data) && $data)
        $content=$data;
    
	// извлекаем переменные $vars, и они перекрывают одноименные переменные из $data
	if ($additional_vars && is_array($additional_vars)) 
		extract($additional_vars, EXTR_REFS | EXTR_SKIP ); 	//не перекрываем существующие переменные + извлекаем их как ссылки, чтобы шаблон мог менять
	
    // если $data - одномерный массив - извлекаем его свойства 
    if ( is_object($data) || (is_array($data) && !isset($data[0])))
	{
    	extract( (array)$data, EXTR_SKIP ); //не перекрываем существующие переменные
	}
	

	// echo "$template_path<br/> ";
	
	// если шаблон не существует - кладем в результат свойство $content 
    if (!$template_path || !file_exists($template_path))    
    {
        $result = @$content;
    }
    else
    {// если шаблон существует - выполняем его и результат получаем их буфера 
	
        $error_level_old = error_reporting(E_ALL&~ E_NOTICE);// для удобства позволяем использовать в шаблоне неинициализированные переменные и индексы без вывода ошибок
        
		//echo "$template_path | ";
		
		// pr(file($template_path));
		
		ob_start();
        include ($template_path);
		//echo "$template_path | ";
		
        $result = ob_get_contents();
        ob_end_clean();
        
        error_reporting($error_level_old);// устанавливаем прежний уровень ошибок
    }
    
    return $result;
}



/*
Выводит пронумерованый код
*/ 
function print_code($code, $select_line='')
{
    $code = "\n".htmlspecialchars($code);
    $code = str_replace("\n","<li>\n",$code);
    $code = "<code>$code</ol></code>";
    echo $code;

}

// возвращает указаную строку из файла или текста 
function get_line($str, $line) 
{ 
	if (is_file($str)) 
	{
		$lines = file($str);
		return isset($lines[$line]) ? $lines[$line] : null;
	}
	
	$str = str_replace( array("\r\n", "\n\r", "\r"), "\n", $str);
	if(!strstr($str, "\n")) return $str;
	
	$lines = explode("\n", $str); 
	return isset($lines[$line]) ? $lines[$line] : null; 
}


/*
While it's easy to change timezones based on names or abbreviations, I haven't found any straightforward way of doing so using an offset integer.  This situation comes up if you're using AJAX to get information about a user's timezone; javascript's getTimezoneOffset() method just sends you an offset number. So, here's my clunky solution: an adaptation of chris' function at http://us.php.net/manual/en/function.timezone-name-from-abbr.php.
*/
function set_tz_by_offset($offset) 
{
    $abbrarray = timezone_abbreviations_list();
    foreach ($abbrarray as $abbr) 
    {
        foreach ($abbr as $city) 
        {
            if ($city['offset'] == $offset) { // remember to multiply $offset by -1 if you're getting it from js
                   date_default_timezone_set($city['timezone_id']);
                   return true;
            }
        }
    }
    date_default_timezone_set("ust");
   return false;
}


// encoding detection (win1251, utf-8) 
// Используя иконв и мд5чексум, мы по-порядку ищем нужную нам кодировку из масива допустимых кодировочек ;) Легко и просто!
function detect_encoding($string) { 
  static $list = array('utf-8', 'windows-1251');
 
  foreach ($list as $item) {
    $sample = iconv($item, $item, $string);
    if (md5($sample) == md5($string))
      return $item;
  }
  return null;
} 

/** 
 * Returns true if $string is valid UTF-8 and false otherwise. 
 * 
 * @since        1.14 
 * @param [mixed] $string     string to be tested 
 * @subpackage 
 */ 
function is_utf8($string) { 
   
	// From http://w3.org/International/questions/qa-forms-utf-8.html 
	return preg_match('%^(?: 
		  [\x09\x0A\x0D\x20-\x7E]            # ASCII 
		| [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte 
		|  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs 
		| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte 
		|  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates 
		|  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3 
		| [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15 
		|  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16 
	)*$%xs', $string); 
   
} 


// Преобразование ДД.ММ.ГГГГ ЧЧ:ММ => Unix timestamp
function datetime2unix($datetime)
{
	$datetime =  trim(str_replace(array('    ','   ','  '),' ',$datetime));
	
	// parse & extract  date,time,month,yeqar,day
	list($date,$time) = explode(' ',$datetime.' ');
	list($day,$month,$year) = explode('.',$date);
	list($hour,$minute) = explode(':',$time.':');
	$second=0;
	//pr("$day,$month,$year,$hour,$minute");
	//pr("\$date=$date, \$time=$time");
	
	// calc timestamp
	$timestamp = mktime (
		(int)$hour, (int)$minute, (int)$second, 
		$month ? $month : date('m'), 
		$day ? $day : date('d'), 
		(trim($year)===''?date('Y'):$year) // год=='' это текущий, год=='00' это 2000 
		);
	
	// check for error data & return
	return  $timestamp > 0 ? $timestamp : ($timestamp<0 && $timestamp!=-1 ? 0 : false);

}

// Преобразование ДД.ММ.ГГГГ ЧЧ:ММ => Unix timestamp
function unix2datetime($timestamp, $return_array=false)
{
	//echo "$timestamp | "; 
	$timestamp=intval($timestamp);
	//echo "$timestamp | "; 
	
	$date = date('j.m.Y', $timestamp);	// '8.08.2008'
	$time = date('G:i', $timestamp); 	// '8:08'
	$datetime = $date .' '. $time;		// '8.08.2008 8:08'
	
	return $return_array? array($date,$time,$datetime) : $datetime;
}					

// вычисляет среднее арифместическое массива


/*** 
	Array array_get( Array $arr, Mixed $key, Mixed $default = null) 
	
	Извлекает из массива $arr элепмент с индексом $key, а если его не сущесвтует, то возвращает $default
	
	Если $key - это массив, то из массива $arr извлекется несколько элементов, а если они не существуют, то вместо них подставляется $default (если это скаляр), или соответствующий $default[$i]  (если $default - это массив)
	
***/	

function array_get($arr, $key, $default = null) 
{
	if (is_array($key))
	{
		$result=array();
		foreach ($key as $i=>$k)
		{
			$d = is_array($default)? $default[$i] : $default;
			$result[$k] = array_get($arr, $k, $d);
		}
		return $result;	
	}
	
	if (array_key_exists($key, $arr)) 
		return $arr[$key];
	else 
		return $default;
}
/* test
$arr = array('a'=>'AAA', 'b'=>'BBB', 'c'=>'CCC');
pr(array_get($arr, array('a','c')));
 */

// преобразуем строку с параметрами (name=val&name2=val2) в массив
function query2array($str)
{
	if (is_string($str)) parse_str($str, $str);
	return $str;
} 
 
 
// trim all elemens of array 
function array_trim($arr, $charlist=null)
{
    foreach($arr as $key => $value)
	{
        if (is_array($value) || is_object($value)) 
			$result[$key] = array_trim($value, $charlist);
        else 
			$result[$key] =  $charlist === NULL
								? trim($value) 
								: trim($value, $charlist);
    }
    return $result;
} 
 
 
function array_average($arr, $dot_digits=10)
{
	if (!$arr || !sizeof($arr) ) return 0;
	return round(array_sum($arr) / sizeof($arr), $dot_digits);
	
}

// возвращает часть строки до последнего включения $needle, не включая  $needle
function strrstr($str, $needle)
{
    return substr($str, 0, strrpos($str,$needle));
}

// возвращает часть строки ПОСЛЕ первого $needle
function strstr_after($haystack, $needle)
{
    return substr(strstr($haystack, $needle), strlen($needle));
}



/**
	preg_find( $expr, $str ) - возращает совпавшую подстроку
  	$expr - регулярное выражение 
	$str - строка
	
	Возвращает - подстроку из $str, совпавшую с первой группой() в выражениии $expr, а если групп нет - то со всем рег выражением.
	
 */
function preg_find( $expr, $str )
{
	preg_match($expr, $str, $found);
	
	if (isset($found[1])) return $found[1];
	if (isset($found[0])) return $found[0];
	return '';
	

}
// test:
// pr( preg_find('!<h1(?:>|\s[^>]*)>(.*)</h1>!i', 
	// '<h1 1>Заголовок</h1>'
// ));


// возвращает строку из языкового массива
// примеры: echo lang('copy') . lang('files/copy') . lang('files/message/error')
// если на входе массив - то его значения добавляются к языковому массиву
function lang($id)
{
	static $lang_array = array();
	if (is_array($id)) {$lang_array = array_merge($lang_array, $id); return;}
	
	list($id1, $id2, $id3) = explode('/', $id.'//');
	if ($id1 && is_string($lang_array[$id1])) 
		return $lang_array[$id1];
	if ($id2 && is_string($lang_array[$id1][$id2])) 
		return $lang_array[$id1][$id2];
	if ($id3 && is_string($lang_array[$id1][$id2][$id3])) 
		return $lang_array[$id1][$id2][$id3];
}
 


